﻿// ***********************************************************************
// Assembly         : DispatcherQueueService
// Author           : Eric Cocquerez
// Created          : 12-20-2014
//
// Last Modified By : Eric Cocquerez
// Last Modified On : 12-20-2014
// ***********************************************************************
// <copyright file="DispatcherQueueService.cs" company="Personnel">
//     Copyright (c) Personnel. All rights reserved.
// </copyright>
// <summary></summary>
// ***********************************************************************
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;

namespace DispatcherQueueService
{
	using System.Collections.ObjectModel;
	using System.Configuration;
	using System.IO;
	using System.Management.Automation;
	using System.Messaging;

	using DispatcherModel;

	/// <summary>
	/// Class DispatcherQueueService.
	/// </summary>
	public partial class DispatcherQueueService : ServiceBase
	{
		/// <summary>
		/// Define the queue name
		/// </summary>
		private const string QueueName = @".\Private$\TfsDispatcher";

		/// <summary>
		/// Flag to manage logs
		/// </summary>
		private Boolean IsLog = false;

		/// <summary>
		/// Initializes a new instance of the <see cref="DispatcherQueueService"/> class.
		/// </summary>
		public DispatcherQueueService()
		{
			this.InitializeComponent();
			this.eventLogDispatcher = new EventLog();
			if (!EventLog.SourceExists("Dispatcher"))
			{
				EventLog.CreateEventSource("Dispatcher","DispatcherLog");
			}
			this.eventLogDispatcher.Source = "Dispatcher";
			this.eventLogDispatcher.Log = "DispatcherLog";
			if (MessageQueue.Exists(QueueName))
			{
				this.messageQueue = new MessageQueue(QueueName);
			}
			else
			{
				this.messageQueue = MessageQueue.Create(QueueName);
			}
			var address = ConfigurationManager.AppSettings["MulticastAddress"];
			if (string.IsNullOrEmpty(address))
			{
				address = "224.34.1.1";
			}
			var port = ConfigurationManager.AppSettings["MulticastPort"];
			if (string.IsNullOrEmpty(port))
			{
				port = "4151";
			}
			this.IsLog = Convert.ToBoolean(ConfigurationManager.AppSettings["IsLog"]);

			this.messageQueue.MulticastAddress = string.Format("{0}:{1}", address, port);
			this.messageQueue.Formatter = new XmlMessageFormatter(new Type[] { typeof(BuildQualityValues) });
			this.messageQueue.ReceiveCompleted += this.MessageQueueReceiveCompleted;
			this.messageQueue.BeginReceive();
			this.eventLogDispatcher.WriteEntry("Starting receive on multicast address : " + address + ":" + port);
		}

		/// <summary>
		/// When implemented in a derived class, executes when a Start command is sent to the service
		/// by the Service Control Manager (SCM) or when the operating system starts
		/// (for a service that starts automatically). Specifies actions to take when the service starts.
		/// </summary>
		/// <param name="args">Data passed by the start command.</param>
		protected override void OnStart(string[] args)
		{
			this.eventLogDispatcher.WriteEntry("Starting service DispatcherQueueService");
		}

		/// <summary>
		/// When implemented in a derived class, executes when a Stop command is sent
		/// to the service by the Service Control Manager (SCM).
		/// Specifies actions to take when a service stops running.
		/// </summary>
		protected override void OnStop()
		{
			this.eventLogDispatcher.WriteEntry("Stopping service DispatcherQueueService");
			this.messageQueue.Close();
			this.messageQueue.ReceiveCompleted -= this.MessageQueueReceiveCompleted;
			if (MessageQueue.Exists(QueueName))
			{
				MessageQueue.Delete(QueueName);
			}
		}

		/// <summary>
		/// Handles the ReceiveCompleted event of the messageQueue control.
		/// </summary>
		/// <param name="sender">The source of the event. It's the message queue</param>
		/// <param name="e">The <see cref="ReceiveCompletedEventArgs"/> instance containing the event data.</param>
		private void MessageQueueReceiveCompleted(object sender, ReceiveCompletedEventArgs e)
		{
			try
			{
				if (this.IsLog)
				{
					this.eventLogDispatcher.WriteEntry("Receiving new message...");
				}
				var queue = sender as MessageQueue;
				if (queue != null)
				{
					var result = (BuildQualityValues)queue.EndReceive(e.AsyncResult).Body;
					var fileName = this.BuildFileName(result);
					if (this.IsLog)
					{
						this.eventLogDispatcher.WriteEntry("Checking existence of file " + fileName);
					}
					if (File.Exists(fileName))
					{
						if (this.IsLog)
						{
							this.eventLogDispatcher.WriteEntry("File exist, execution powershell file");
						}

						using (PowerShell powerShell = PowerShell.Create())
						{
							var fileScript = new StreamReader(fileName);
							var script = fileScript.ReadToEnd();
							fileScript.Close();
							powerShell.AddScript(script);
							powerShell.AddParameter("TeamProject", result.TeamProject);
							powerShell.AddParameter("OldQuality", result.OldValue);
							powerShell.AddParameter("NewQuality", result.NewValue);
							powerShell.AddParameter("DropLocation", result.DropLocation);
							powerShell.AddParameter("BuildNumber", result.BuildNumber);
							powerShell.AddParameter("BuildDefinitionName", result.BuildDefinitionName);
							powerShell.AddParameter("CompilationStatus", result.CompilationStatus);
							powerShell.AddParameter("BuildControlerURI", result.BuildControllerUri);
							powerShell.AddParameter("BuildDefinitionURI", result.BuildDefinitionUri);
							powerShell.AddParameter("ContainerId", result.ContainerId);

							var output = powerShell.Invoke();
							foreach (var returnedInformation in output)
							{
								Console.WriteLine(returnedInformation.ToString());
							}
						}
					}
					else
					{
						if (this.IsLog)
						{
							this.eventLogDispatcher.WriteEntry("File doesn't exist, trying to create it");
						}
						try
						{
							var writer = new StreamWriter(fileName);
							writer.Write("Param(\r\n\t[string]$TeamProject,\r\n\t[string]$OldQuality,\r\n\t[string]$NewQuality,\r\n\t[string]$DropLocation,\r\n\t[string]$BuildNumber,\r\n\t[string]$BuildDefinitionName,\r\n\t[string]$CompilationStatus,\r\n\t[string]$BuildControlerURI,\r\n\t[string]$BuildDefinitionURI,\r\n\t[string]$ContainerId\r\n)\r\n");
							writer.Flush();
							writer.Close();
							if (this.IsLog)
							{
								this.eventLogDispatcher.WriteEntry("File created and saved : " + fileName);
							}
						}
						catch (Exception exception)
						{
							this.eventLogDispatcher.WriteEntry("Error while writing file " + fileName + " Exception : " + exception.Message);
						}
					}
					queue.BeginReceive();
				}
			}
			catch (Exception ex)
			{
				this.eventLogDispatcher.WriteEntry(ex.Message);
			}
		}

		/// <summary>
		/// Builds the complete file name based on conventions.
		/// </summary>
		/// <param name="buildQualityValues">The build quality values.</param>
		/// <returns>System.String.</returns>
		private string BuildFileName(BuildQualityValues buildQualityValues)
		{
			var oldQuality = string.Empty;
			var newQuality = string.Empty;

			var basePath = ConfigurationManager.AppSettings["ScriptDirectory"];
			if (string.IsNullOrEmpty(basePath))
			{
				basePath = "c:\\TfsDeployerScript\\";
			}
			if (!basePath.EndsWith("\\"))
			{
				basePath += "\\";
			}

			if (string.IsNullOrEmpty(buildQualityValues.OldValue))
			{
				oldQuality = "Clear";
			}
			else
			{
				oldQuality = new string(buildQualityValues.OldValue.Where(c => !char.IsWhiteSpace(c)).ToArray());
			}

			if (string.IsNullOrEmpty(buildQualityValues.NewValue))
			{
				newQuality = "Clear";
			}
			else
			{
				newQuality = new string(buildQualityValues.NewValue.Where(c => !char.IsWhiteSpace(c)).ToArray());
			}

			var directory = string.Format("{0}{1}\\{2}", basePath, buildQualityValues.TeamProject, buildQualityValues.BuildDefinitionName);
			if (!Directory.Exists(directory))
			{
				try
				{
					Directory.CreateDirectory(directory);
				}
				catch (Exception exception)
				{
					this.eventLogDispatcher.WriteEntry("Error while creating directory " + directory + " Exception : " + exception.Message);
				}

				Directory.CreateDirectory(directory);
			}

			var filename = string.Format("{0}\\{1}To{2}.ps1",directory, oldQuality, newQuality);
			return filename;
		}
	}
}
